/*
 * edl_trav.c
 * Clunix, cezanne@clunix.com, 2005
 *
 * Copyright (c) 2001-2008 by Clunix Inc. All Rights Reserved.
 */

#include "config.h"

#include "edl.h"

static void
traverse_child(const sr_t *sr_edis, edl_travfunc_t func, ecm_ptr_t ctx)
{
	const sr_t	*sr_edi;

	SR_FOREACH_CHILD(sr_edis, NULL, sr_edi)
		edl_traverse(sr_edi, func, ctx);
}

static void
trav_binary_func(const sr_t *sr_edi, edl_travfunc_t func, ecm_ptr_t ctx)
{
	sr_t	*sr_args;

	sr_args = sr_geti(sr_edi, 2);
	edl_traverse(sr_geti(sr_args, 0), func, ctx);
	edl_traverse(sr_geti(sr_args, 1), func, ctx);
}

static void
trav_unary_func(const sr_t *sr_edi, edl_travfunc_t func, ecm_ptr_t ctx)
{
	edl_traverse(sr_geti(sr_edi, 1), func, ctx);
}

static void
trav_basefunc(const sr_t *sr_edi, edl_travfunc_t func, ecm_ptr_t ctx)
{
	int	fid = sr_geti_int(sr_edi, 1);

	switch (fid) {
	case EQUAL_FID:
	case NOTEQUAL_FID:
	case PLUS_FID:
	case MINUS_FID:
	case GREATERTHAN_FID:
	case LESSTHAN_FID:
	case GREATEREQUAL_FID:
	case LESSEQUAL_FID:
		trav_binary_func(sr_edi, func, ctx);
		break;
	case LOGICALNOT_FID:
		trav_unary_func(sr_edi, func, ctx);
		break;
	default:
		break;
	}
}

static void
trav_userfunc(const sr_t *sr_edi, edl_travfunc_t func, ecm_ptr_t ctx)
{
	sr_t	*sr_args;

	sr_args = sr_geti(sr_edi, 2);
	traverse_child(sr_args, func, ctx);
}

static void
trav_func(const sr_t *sr_edi, edl_travfunc_t func, ecm_ptr_t ctx)
{
	if (sr_geti_type(sr_edi, 1) == ST_INT)
		trav_basefunc(sr_edi, func, ctx);
	else
		trav_userfunc(sr_edi, func, ctx);
}

static void
trav_args(const sr_t *sr_edi, edl_travfunc_t func, ecm_ptr_t ctx)
{
	traverse_child(sr_getc(sr_edi, "args"), func, ctx);
}

static void
trav_subentity(const sr_t *sr_edi, edl_travfunc_t func, ecm_ptr_t ctx)
{
	sr_t	*sr_args;

	sr_args = sr_getc(sr_edi, "args");
	edl_traverse(sr_geti(sr_args, 0), func, ctx);
}

static void
trav_call(const sr_t *sr_edi, edl_travfunc_t func, ecm_ptr_t ctx)
{
	edl_traverse(sr_geti(sr_edi, 1), func, ctx);
	traverse_child(sr_geti(sr_edi, 2), func, ctx);
}

static void
trav_case(const sr_t *sr_edi, edl_travfunc_t func, ecm_ptr_t ctx)
{
	const sr_t	*sr_case_body, *sr_case_exec;
	sr_t		*sr_args;

	sr_args = sr_getc(sr_edi, "args");
	edl_traverse(sr_geti(sr_args, 0), func, ctx);
	sr_case_body = sr_geti(sr_args, 1);
	SR_FOREACH_CHILD(sr_case_body, NULL, sr_case_exec) {
		edl_traverse(sr_geti(sr_case_exec, 0), func, ctx);
		edl_traverse(sr_geti(sr_case_exec, 1), func, ctx);
	}
}

static void
trav_cast(const sr_t *sr_edi, edl_travfunc_t func, ecm_ptr_t ctx)
{
	sr_t	*sr_args;

	sr_args = sr_getc(sr_edi, "args");
	edl_traverse(sr_geti(sr_args, 1), func, ctx);
}

typedef void (*edi_travfunc_t)(const sr_t *, edl_travfunc_t, ecm_ptr_t);

static edi_travfunc_t	edi_travfuncs[] = {
	trav_func,
	NULL,			/* EDI_ASRTV */
	NULL,
	NULL,
	trav_args,		/* EDI_SEQUENTIAL */
	trav_args,		/* EDI_VECTOR */
	trav_args,		/* EDI_VECTOR1 */
	trav_args,		/* EDI_COND */
	trav_args,		/* EDI_CNP */
	trav_args,		/* EDI_SET */
	trav_args,		/* EDI_XECREATE */
	trav_args,		/* EDI_XEDELETE */
	trav_subentity,		/* EDI_SUBENTITY */
	trav_args,		/* EDI_SAME */
	trav_args,		/* EDI_NOTSAME */
	trav_args,		/* EDI_NAME */
	trav_args,		/* EDI_CHILDCNT */
	trav_args,		/* EDI_ACTUATOR */
	trav_args,		/* EDI_EXIST */
	trav_args,		/* EDI_VECTORPLUS */
	trav_args,		/* EDI_VECTORMINUS */
	trav_args,		/* EDI_OWNER */
	trav_args,		/* EDI_FILE */
	trav_args,		/* EDI_LOGICALAND */
	trav_args,		/* EDI_LOGICALOR */
	trav_args,		/* EDI_HOMOSET */
	trav_args,		/* EDI_HOMOSETHAS */
	trav_call,		/* EDI_CALL */
	trav_call,
	NULL,
	NULL,
	NULL,
	trav_args,		/* EDI_SELFUSER */
	trav_args,		/* EDI_WHILE */
	trav_case,		/* EDI_CASE */
	NULL,			/* EDI_CASEVAR */
	trav_cast,		/* EDI_CAST */
	trav_args,		/* EDI_REXEC */
	trav_args,		/* EDI_CS */
	trav_args,		/* EDI_BITWISEOR */
	NULL,			/* EDI_CTXVALVAR */
	trav_args,		/* EDI_CNP1 */
};

void
edl_traverse(const sr_t *sr_edi, edl_travfunc_t func, ecm_ptr_t ctx)
{
	int	edi_type;
	edi_travfunc_t	edi_travfunc;

	if (sr_edi == NULL)
		return;
	edi_type = sr_geti_int(sr_edi, 0);

	if (!func(sr_edi, ctx))
		return;

	edi_travfunc = edi_travfuncs[edi_type - 1];
	if (edi_travfunc)
		edi_travfunc(sr_edi, func, ctx);
}
